import base64
import json
import pickle

from django import http
from django.shortcuts import render

# Create your views here.
from django.views import View
from django_redis import get_redis_connection

from apps.goods.models import SKU
from utils.response_code import RETCODE

"""
登录用户和未登录用户均可以添加购物车
    未登录用户必须保存的数据：sku_id(商品id)，count(个数)，selected(选中状态)
                            数据保存在cookie中
                            {
                                'sku_id': {'count': xxx, 'selected': true/false}
                                'sku_id': {'count': xxx, 'selected': true/false}
                                'sku_id': {'count': xxx, 'selected': true/false}
                            }
                            
    登录用户必须保存的数据：user_id(用户id)
                        sku_id(商品id)，count(个数)，selected(选中状态)
                        数据保存在Redis中
                        hash
                        user_id
                            sku_id:count
                            selected_sku_id:selected

字典数据加密（编码）处理:
    字典---->bytes(二进制)类型---->base64编码          
"""


class CartView(View):
    """购物车管理"""

    def get(self, request):
        """展示购物车"""
        # 1、获取用户信息，根据用户信息进行判
        user = request.user
        if user.is_authenticated:
            # 2、登录用户：Redis查询
            # 2.1、链接Redis
            redis_conn = get_redis_connection('carts')
            # 2.2、hash
            sku_id_count = redis_conn.hgetall('carts_%s' % user.id)
            # 2.3、set
            selected_ids = redis_conn.smembers('selected_%s' % user.id)

            carts = {}
            for sku_id, count in sku_id_count.items():
                # 判断是否为选中状态
                if sku_id in selected_ids:
                    selected = True
                else:
                    selected = False
                # 组织添加新数据
                carts[int(sku_id)] = {
                    'count': int(count),
                    'selected': selected,
                }

        else:
            # 3、未登录用户：cookie
            # 3.1、读取cookie
            cookie_str = request.COOKIES.get('carts')
            # 3.2、判断cart数据，如果有则解码；如果没有则初始化一个字典
            if cookie_str is None:
                carts = {}
            else:
                de = base64.b64decode(cookie_str)
                carts = pickle.loads(de)

        # 4、根据ID查询商品信息
        ids = carts.keys()
        skus = SKU.objects.filter(id__in=ids)

        sku_list = []
        total_count = 0  # 总数量
        total_amount = 0  # 总价格
        for sku in skus:
            sku_list.append({
                'id': sku.id,
                'name': sku.name,
                'count': carts.get(sku.id).get('count'),
                'selected': str(carts.get(sku.id).get('selected')),  # 将True，转'True'，方便json解析
                'default_image_url': sku.default_image.url,
                'price': str(sku.price),  # 从Decimal('10.2')中取出'10.2'，方便json解析
                'amount': str(sku.price * carts.get(sku.id).get('count')),
            })
            total_count += carts[sku.id]['count']
            total_amount += (sku.price * carts[sku.id]['count'])
        # 5、展示
        context = {
            'cart_skus': sku_list,
        }
        return render(request, 'cart.html', context=context)

    def post(self, request):
        """新增购物车"""
        # 1、接收数据
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        # 2、验证数据
        # 2.1、判断参数是否齐全
        if not all([sku_id, count]):
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数不齐'})
        # 2.2、判断商品是否存在
        try:
            sku = SKU.objects.get(pk=sku_id)
        except SKU.DoesNotExist:
            return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '暂无此数据'})
        # 2.3、数量得是个数值
        try:
            count = int(count)
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数异常'})
        # 3、根据用户是否登录来保存
        if request.user.is_authenticated:
            # 4、登录用户：Redis
            # 4.1、链接Redis
            redis_conn = get_redis_connection('carts')
            # ① 创建管道
            pl = redis_conn.pipeline()
            # 4.2、hash
            pl.hincrby('carts_%s' % request.user.id, sku_id, count)
            # 4.3、set
            pl.sadd('selected_%s' % request.user.id, sku_id)
            # ③ 执行管道
            pl.execute()
            # 4.4、返回相应
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'})
        else:
            # 5、未登录用户：cookie
            # 5.0、判断cookie数据是否存在
            cookie_str = request.COOKIES.get('carts')
            if cookie_str is None:
                # 如果不存在，初始化一个空的carts
                carts = {}
                pass
            else:
                # 如果存在，对数据解码
                de = base64.b64decode(cookie_str)
                carts = pickle.loads(de)
                pass

            # 5.1、更新数据
            if sku_id in carts:
                # 数量累加
                origin_count = carts[sku_id]['count']
                count += origin_count

            carts[sku_id] = {
                'count': count,
                'selected': True,
            }
            # 5.2、对数据进行base64处理
            d = pickle.dumps(carts)  # 字典转换为byte类型
            en = base64.b64encode(d)  # base64编码
            # 5.3、设置cookie
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'})
            response.set_cookie('carts', en.decode())
            # 5.4、返回响应
            return response

    def put(self, request):
        """更新购物车"""
        # 1、接收数据
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')
        count = data.get('count')
        selected = data.get('selected')
        # 2、验证数据
        # 2.1、判断参数是否齐全
        if not all([sku_id, count]):
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数不齐'})
        # 2.2、判断商品是否存在
        try:
            sku = SKU.objects.get(pk=sku_id)
        except SKU.DoesNotExist:
            return http.JsonResponse({'code': RETCODE.NODATAERR, 'errmsg': '暂无此数据'})
        # 2.3、数量得是个数值
        try:
            count = int(count)
        except Exception as e:
            return http.JsonResponse({'code': RETCODE.PARAMERR, 'errmsg': '参数异常'})

        # 3、获取用户的信息
        if request.user.is_authenticated:
            # 4、登录用户：更新Redis数据
            redis_conn = get_redis_connection('carts')
            redis_conn.hset('carts_%s' % request.user.id, sku_id, count)
            if selected:
                redis_conn.sadd('selected_%s' % request.user.id, sku_id)
            else:
                redis_conn.srem('selected_%s' % request.user.id, sku_id)

            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok', 'cart_sku': cart_sku})
        else:
            # 5、未登录用户：更新cookie数据
            cookie_str = request.COOKIES.get('carts')
            if cookie_str is None:
                carts = {}
            else:
                carts = pickle.loads(base64.b64decode(cookie_str))
            if sku_id in carts:
                carts[sku_id] = {
                    'count': count,
                    'selected': selected,
                }
            en = base64.b64encode(pickle.dumps(carts))
            cart_sku = {
                'id': sku_id,
                'count': count,
                'selected': selected,
                'name': sku.name,
                'default_image_url': sku.default_image.url,
                'price': sku.price,
                'amount': sku.price * count,
            }
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok', 'cart_sku': cart_sku})
            response.set_cookie('carts', en)
            return response

    def delete(self, request):
        data = json.loads(request.body.decode())
        sku_id = data.get('sku_id')

        if request.user.is_authenticated:
            # 登录用户：删除redis
            redis_conn = get_redis_connection('carts')
            redis_conn.hdel('carts_%s' % request.user.id, sku_id)
            redis_conn.srem('selected_%s' % request.user.id, sku_id)
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'})

        else:
            # 未登录用户：删除cookie
            cookie_str = request.COOKIES.get('carts')
            if cookie_str is None:
                carts = {}
            else:
                carts = pickle.loads(base64.b64decode(cookie_str))
            if sku_id in carts:
                del carts[sku_id]

            en = base64.b64encode(pickle.dumps(carts))
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'ok'})
            response.set_cookie('carts', en.decode())
            return response


class CartsSelectAllView(View):
    """全选购物车"""

    def put(self, request):
        # 接收参数
        json_dict = json.loads(request.body.decode())
        selected = json_dict.get('selected', True)

        # 校验参数
        if selected:
            if not isinstance(selected, bool):
                return http.HttpResponseBadRequest('参数selected有误')

        # 判断用户是否登录
        user = request.user
        if user is not None and user.is_authenticated:
            # 用户已登录，操作redis购物车
            redis_conn = get_redis_connection('carts')
            cart = redis_conn.hgetall('carts_%s' % user.id)
            sku_id_list = cart.keys()
            if selected:
                # 全选
                redis_conn.sadd('selected_%s' % user.id, *sku_id_list)
            else:
                # 取消全选
                redis_conn.srem('selected_%s' % user.id, *sku_id_list)
            return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})
        else:
            # 用户已登录，操作cookie购物车
            cart = request.COOKIES.get('carts')
            response = http.JsonResponse({'code': RETCODE.OK, 'errmsg': '全选购物车成功'})
            if cart is not None:
                cart = pickle.loads(base64.b64decode(cart.encode()))
                for sku_id in cart:
                    cart[sku_id]['selected'] = selected
                cookie_cart = base64.b64encode(pickle.dumps(cart)).decode()
                response.set_cookie('carts', cookie_cart)

            return response


class CartsSimpleView(View):
    """商品页面右上角购物车"""

    def get(self, request):
        # 判断用户是否登录
        user = request.user
        if user.is_authenticated:
            # 用户已登录，查询Redis购物车
            redis_conn = get_redis_connection('carts')
            redis_cart = redis_conn.hgetall('carts_%s' % user.id)
            cart_selected = redis_conn.smembers('selected_%s' % user.id)
            # 将redis中的两个数据统一格式，跟cookie中的格式一致，方便统一查询
            cart_dict = {}
            for sku_id, count in redis_cart.items():
                cart_dict[int(sku_id)] = {
                    'count': int(count),
                    'selected': sku_id in cart_selected
                }
        else:
            # 用户未登录，查询cookie购物车
            cart_str = request.COOKIES.get('carts')
            if cart_str:
                cart_dict = pickle.loads(base64.b64decode(cart_str.encode()))
            else:
                cart_dict = {}

        # 构造简单购物车JSON数据
        cart_skus = []
        sku_ids = cart_dict.keys()
        skus = SKU.objects.filter(id__in=sku_ids)
        for sku in skus:
            cart_skus.append({
                'id': sku.id,
                'name': sku.name,
                'count': cart_dict.get(sku.id).get('count'),
                'default_image_url': sku.default_image.url
            })

        # 响应json列表数据
        return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'cart_skus': cart_skus})


